﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using System.Threading;
using Landscape.Business;
using Landscape.Business.FNH;
using Landscape.Business.FNH.Activities;
using System.Collections.Concurrent;

namespace Landscape.WebApp.SignalR
{
    public class Chat
    {
        private readonly static Lazy<Chat> myObject = new Lazy<Chat>(() => new Chat(GlobalHost.ConnectionManager.GetHubContext<ERMChatHub>().Clients));

        private readonly ConcurrentDictionary<int, ChatItem> chatLog = new ConcurrentDictionary<int, ChatItem>();
        private readonly object chatUpdateLock = new object();


        private Chat(IHubConnectionContext pClients)
        {
            // TODO: Complete member initialization
            this.Clients = pClients;
            this.chatLog.Clear();
        }

        public static Chat Instance
        {
            get { return myObject.Value; }
        }

        private IHubConnectionContext Clients
        {
            get;
            set;
        }

        public IList<ChatItem> GetChatLogAfter(int pLastItemID, Guid pIncID)
        {
            if (pLastItemID < 0)
                pLastItemID = 0;

            if (this.chatLog.Where(exp => exp.Value.IncidentCode == pIncID).Count() == 0)
            {
                using (var nhSession = NHSessionManager.GetManager().GetSession())
                {
                    var erActs = new EmergencyActivities(nhSession);
                    var inci = erActs.GetIncidentByCode(pIncID);
                    if (inci.ScribeLog != null)
                    {
                        foreach (var item in inci.ChatLog.OrderBy(exp => exp.MessageIndex))
                        {
                            ChatItem chatItem = new ChatItem();
                            chatItem.CreatedBy = item.CreatedBy;
                            chatItem.IncidentCode = pIncID;
                            chatItem.Message = item.Message;
                            chatItem.ConversationCode = (item.ConversationId == Guid.Empty ? Guid.NewGuid() : item.ConversationId);
                            chatItem.ParentChatItemCode = item.ParentId;
                            chatItem.CreatedDate = item.CreatedDate;
                            chatItem.ChatItemCode = item.ID;

                            var udet = new AdminActivities().GetUserMasterByID(chatItem.CreatedBy);
                            chatItem.CreatedUser = udet.FirstName + " " + udet.LastName;

                            
                            this.chatLog.TryAdd((this.chatLog.Count > 0 ? this.chatLog.Max(exp => exp.Key) + 1 : 1), chatItem);
                        }
                    }
                }
            }

            return this.chatLog.Where(exp => exp.Key > pLastItemID && exp.Value.IncidentCode == pIncID).Select(exp => exp.Value).ToList();
        }

        public void AddToChatLog(ChatItem pItem)
        {
            lock (chatUpdateLock)
            {
                var nextMaxId = (this.chatLog.Count > 0 ? this.chatLog.Max(exp => exp.Key) + 1 : 1);
                pItem.ChatItemCode = SaveToDataBase(pItem);
                this.chatLog.TryAdd(nextMaxId, pItem);

                var udet = new AdminActivities().GetUserMasterByID(pItem.CreatedBy);
                pItem.CreatedUser = udet.FirstName + " " + udet.LastName;

                BroadcastToClients(pItem);
            }
        }

        private void BroadcastToClients(ChatItem pItem)
        {
            this.Clients.Group(pItem.IncidentCode.ToString()).updateChatLog(pItem);
        }

        private Guid SaveToDataBase(ChatItem pItem)
        {
            Guid id = Guid.Empty;

            using (var nhSession = NHSessionManager.GetManager().GetSession())
            {
                using (var trans = nhSession.BeginTransaction())
                {
                    var erActs = new EmergencyActivities(nhSession);
                    var inci = erActs.GetIncidentByCode(pItem.IncidentCode);

                    Landscape.Business.Entities.ChatItem curScribe = new Business.Entities.ChatItem();
                    curScribe.CreatedBy = pItem.CreatedBy;
                    curScribe.CreatedDate = DateTime.Now;
                    curScribe.Message = pItem.Message;
                    if (pItem.ParentChatItemCode != Guid.Empty)
                        curScribe.ParentId = pItem.ParentChatItemCode;
                    curScribe.ConversationId = (pItem.ConversationCode == Guid.Empty ? Guid.NewGuid() : pItem.ConversationCode);

                    if (inci.ChatLog != null && inci.ChatLog.Count > 0)
                    {
                        curScribe.MessageIndex = inci.ChatLog.Count + 1;
                    }
                    else
                    {
                        curScribe.MessageIndex = 1;
                    }
                    
                    inci.ChatLog.Add(curScribe);

                    erActs.SaveIncident(inci);

                    id = erActs.GetIncidentByCode(inci.ID).ChatLog.Where(exp => exp.MessageIndex == curScribe.MessageIndex).Single().ID;

                    trans.Commit();

                    return id;
                }
            }
        }

        internal void ClearEntriesFor(Guid incID)
        {
            var keys = this.chatLog.Where(exp => exp.Value.IncidentCode == incID).Select(exp => exp.Key).ToArray(); ;
            ChatItem itemRemoved = new ChatItem();
            foreach (var key in keys)
            {
                this.chatLog.TryRemove(key, out itemRemoved);
            }
        }
    }
}